#include "song.h"

#include <algorithm>
#include <limits>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/tokenizer.hpp>

using std::vector;
using std::string;
using std::cout;
using std::endl;

Song::Song() : duration(0) {}

Song::Song(int duration) : duration(duration) {}

Song::Song(string & filePath) : duration(-1)
{
	cout << "Loading song from file " << filePath << endl;
	std::ifstream file;
	file.open(filePath.c_str());
	std::stringstream ss;
	string line, field;
	int maxTime = duration;
	bool parsingObstacles = false;
	if(file.is_open()) {
		while(!file.eof() && !parsingObstacles) {
			std::getline(file, line);
			ss.str(line);
			std::getline(ss, field, ' ');
			if(field == "obstacles") {
				parsingObstacles = true;
			}
			// TODO: read duration, name, etc.
		}
		while(!file.eof()) { // obstacles
			int time, duration, size1, size2;
			string typeString;
			Obstacle::Type type;
			std::istringstream iss;
			std::getline(file, line);
			if(line != "") {
				cout << "Obstacle : " << line << endl;
				boost::tokenizer<> tok(line);
				boost::tokenizer<>::iterator beg = tok.begin();
				iss.clear();
				iss.str(*beg);
				iss >> time;
				++beg;
				iss.clear();
				iss.str(*beg);
				iss >> duration;
				++beg;
				typeString = *beg;
				if(typeString == "top") {
					type = Obstacle::Top;
				} else if(typeString == "bottom") {
					type = Obstacle::Bottom;
				} else if(typeString == "left") {
					type = Obstacle::Left;
				} else if(typeString == "right") {
					type = Obstacle::Right;
				} else if(typeString == "topleft") {
					type = Obstacle::TopLeft;
				} else if(typeString == "topright") {
					type = Obstacle::TopRight;
				} else if(typeString == "bottomleft") {
					type = Obstacle::BottomLeft;
				} else if(typeString == "bottomright") {
					type = Obstacle::BottomRight;
				} else {
					type = Obstacle::Bottom;
				}
				++beg;
				iss.clear();
				iss.str(*beg);
				iss >> size1;
				++beg;
				if(beg != tok.end()) {
					iss.clear();
					iss.str(*beg);
					iss >> size2;
				} else {
					size2 = 0;
				}
				Obstacle * obstacle = new Obstacle(type, size1, size2, time, duration);
				obstacles.push_back(obstacle);
				if(time + duration > maxTime) {
					maxTime = time + duration;
				}
			}
		}
		if(maxTime > duration) {
			duration = maxTime;
		}
		file.close();
	} else {
		cout << "Couldn't open the file!" << endl;
	}

}

Song::~Song()
{
	for(vector<Obstacle*>::iterator it = obstacles.begin() ; it != obstacles.end() ; ++it) {
		delete *it;
	}
};

void Song::addObstacle(Obstacle::Type type, int size1, int size2, int time, int duration)
{
	Obstacle * obstacle = new Obstacle(type, size1, size2, time, duration);
	obstacles.push_back(obstacle);
}

void Song::sortObstacles()
{
	std::sort(obstacles.begin(), obstacles.end());
}

int Song::getObstacleCount() const
{
	return obstacles.size();
	//return 0;
}

int Song::getDuration() const
{
	return duration;
}

Song::Iterator Song::obstaclesFrom(int time) const
{
	if(time > duration) {
		return Iterator::end();
	}
	return Iterator(this, time);
}

Song::Iterator Song::endIterator() const
{
	return Iterator(this, -1);
}


// Song iterator

Song::Iterator Song::Iterator::end()
{
	return Song::Iterator();
}

Song::Iterator::Iterator() : song(NULL), time(std::numeric_limits<int>::max()), endTag(true) {}

Song::Iterator::Iterator(const Song * song, int time) : song(song), time(time)
{
	if(song->obstacles.size() != 0) {
		endTag = false;
		it = song->obstacles.begin();
		setPosition(time);
	} else {
		it = song->obstacles.end();
		endTag = true;
		time = std::numeric_limits<int>::max();
	}
}

Song::Iterator::Iterator(Song::Iterator & other) : song(other.song), time(other.time), endTag(other.endTag), it(other.it) {}

void Song::Iterator::setPosition(int time)
{
	if(endTag) {
		return;
	}
	if(it == song->obstacles.end()) {
		endTag = true;
		return;
	}

	while((*it)->getTime() < time) {
		++it;
		if(it == song->obstacles.end()) {
			endTag = true;
			time = std::numeric_limits<int>::max();
			return;
		}
	}
}

int Song::Iterator::getPosition() const
{
	return time;
}

Obstacle * Song::Iterator::operator *() const
{
	if(endTag == true) {
		return NULL;
	}
	return *it;
}

Song::Iterator & Song::Iterator::operator ++()
{
	if(!endTag && song != NULL) {
		++it;
		if(it == song->obstacles.end()) {
			endTag = true;
			time = std::numeric_limits<int>::max();
		} else {
			time = (*it)->getTime();
		}
	}
	return *this;
}

bool Song::Iterator::operator ==(Song::Iterator & rhs)
{
	return (endTag && rhs.endTag) || (!endTag && !rhs.endTag && song == rhs.song && time - rhs.time < 0.000000001);
}

bool Song::Iterator::operator !=(Song::Iterator & rhs)
{
	return !(*this == rhs);
}
